import AStarPoint from "./ASatrPoint";
import SimpleObjectPool from "../Tool/Pool/SimpleObjectPool";
import MathTool from "../Tool/MathTool";



export default class AStarMapManager 
{
    private static m_instance;
    public static GetInstance():AStarMapManager
    {
        if(this.m_instance==null)
        {
            this.m_instance=new AStarMapManager();
        }
        return this.m_instance;
    }


    private  m_mapWidth:number = 8;
    private  m_mapHeight:number = 6;

    public  m_Prefab:Laya.Sprite3D;
    public  m_Maparray:AStarPoint[][]=[];
    private m_internal:number=10;
    private m_currentScene:Laya.Scene3D;

    private m_enemyMinIndexY=14;
    private m_playerMaxIndexY=9;

    
    private  m_scene:Laya.Scene3D;

    private m_MapCubePool:SimpleObjectPool<AStarPoint>;
    private m_visualization:boolean=false;

    public m_MapInitCallBack:Function=null;

    
    public InitMap(currenScene:Laya.Scene3D,startPos:Laya.Vector3,widthcount:number=200,heightcount:number=200,internal:number=20):void
    {       
        this.m_currentScene=currenScene;

        this.ResetArMap();

        if (this.m_MapCubePool==null) 
        {
            this.m_MapCubePool=new SimpleObjectPool(()=>{
            let arpoint:AStarPoint=this.InitPrefab(this.m_currentScene).getComponent(AStarPoint);
            arpoint.MySprite.active=false;
            return arpoint;
           });
        }

        this.m_Maparray=[];
        this.m_mapWidth=Math.floor(widthcount/internal); 
        this.m_mapHeight=Math.floor(heightcount/internal);
        this.m_internal=internal;    
        this.InitStartPoints(startPos,internal);
        this.InitPointByFrame();
       // Laya.timer.frameOnce(1,this,this.InitStartPoints,[mapParentNode,startPos,internal]);     
    }


    private m_nodeIndex=0;
    private m_nodeIndey=0;
    private m_nodecount=0;
    InitStartPoints(startPos:Laya.Vector3,internal:number=20)
    {
        for(var i =0;i < this.m_mapWidth;i++)
        {
            this. m_Maparray[i] = [];
            for(var j = 0;j <this.m_mapHeight; j++)
            {
                let pos=new Laya.Vector3();
                Laya.Vector3.add(startPos,new Laya.Vector3(this.m_internal * i,0,this.m_internal * j),pos);              
                let point=this.m_MapCubePool.Allocate();
                point.MySprite.transform.position=pos;
                this.m_Maparray[i][j]=point;
                this.m_Maparray[i][j].SetMeshEnable(this.m_visualization);
                this.m_Maparray[i][j].SetScalByN3(internal-0.2,3,internal-0.2);
                this.m_Maparray[i][j].Pos=pos;
                this.m_Maparray[i][j].InitPoint(i,j);
            } 
        }  
    }


    InitPointByFrame()
    { 
        if (this.m_nodeIndey>=this.m_mapHeight) 
        {
            this.m_nodeIndey=0;
            this.m_nodeIndex++;
        }
        for(let i =this.m_nodeIndex;i < this.m_mapWidth; )
        {  
            for(let j =  this.m_nodeIndey;j <this.m_mapHeight; this.m_nodeIndey++)
            {
                this.m_nodecount++;
            //    console.log(" this.m_nodecount="+ this.m_nodecount+" this.m_nodeIndex="+this.m_nodeIndex+" this.m_nodeIndey"+this.m_nodeIndey+" i="+i+" j="+j);
                if (  this.m_Maparray[this.m_nodeIndex][ this.m_nodeIndey]!=null) 
                {
                    this.m_Maparray[this.m_nodeIndex][ this.m_nodeIndey].MySprite.active=true;
                }
                if (this.m_nodecount>=10) 
                {
                    this.m_nodecount=0;
                    Laya.timer.frameOnce(2,this,this.InitPointByFrame.bind(this));
                    return;
                }
            }
        }
        this.m_nodeIndex=0;
        this.m_nodeIndey=0;
        Laya.timer.frameOnce(3,this,this.m_MapInitCallBack);
    }



    public GetRandomPosByIndexY(indexYMin:number,IndexYMax:number)
    {
        let xindex=MathTool.GetRandomInt(0,this.m_mapWidth);
        let yindex=MathTool.GetRandomInt(indexYMin,IndexYMax);
        let point=this.m_Maparray[xindex][yindex];
        if (point.IsWall)
        {
          return  this.GetRandomPosByIndexY(indexYMin,IndexYMax);
        }
        return point.Pos;
    }



    //获取左右可以移动的边框
    public GetBorderPosByPos(isLeft:boolean,pos:Laya.Vector3)
    {
        if (isLeft) 
        {
            let point= this.GetPointByPos(pos);
            let index=0;

            this.m_Maparray[index][point.IndexY].ChangeColor(1,1,1,1);
            return this.m_Maparray[index][point.IndexY];
        }
        else
        {
            let point= this.GetPointByPos(pos);
            let index=this.m_mapWidth-1;
            this.m_Maparray[index][point.IndexY].ChangeColor(1,1,1,1);
            return this.m_Maparray[index][point.IndexY];
        }
    }


    InitPrefab(parent:Laya.Node):Laya.Sprite3D
    {
        if(this.m_Prefab==null)
        {
          //  let mapCubePrefab:Laya.Sprite3D=Laya.loader.getRes(ResourceLoader.s_MapCubePath);
           // this.m_currentScene.addChild(mapCubePrefab);
           // this.m_Prefab=mapCubePrefab.getChildByName("MapCube") as Laya.Sprite3D;
            this.m_Prefab.active=false;
            this.m_Prefab.addComponent(AStarPoint);
        }
        let prefab:Laya.Sprite3D=Laya.Sprite3D.instantiate(this.m_Prefab, parent);
        prefab.active=true;
        return prefab;
    }


    public ClearData():void
    {
        this.m_Maparray=[];
    }


    ///获取格子点
    public  GetPointByPos( pos:Laya.Vector3):AStarPoint
    {
        let startx:number=this.m_Maparray[0][0].Pos.x-this.m_internal/2;
        let startz:number=this.m_Maparray[0][0].Pos.z-this.m_internal/2;

        let indexx:number=Math.floor(Math.abs(pos.x-startx) /this.m_internal);
        let indexy:number=Math.floor(Math.abs(pos.z-startz) /this.m_internal);

        indexx=MathTool.Clamp(indexx,0, this.m_mapWidth-1);
        indexy= MathTool.Clamp(indexy, 0, this.m_mapHeight - 1);

        return this.m_Maparray[indexx][indexy];
    }


    public  GetSurroundPoins( point:AStarPoint):Array<AStarPoint>
    {
        // console.log("GetSurroundPoins=="+point.IndexX+" y="+point.IndexY+" f="+point.GetF());
        let up:AStarPoint = null, down:AStarPoint = null, left:AStarPoint = null, right:AStarPoint = null;
        //左上 右上 左下 右下
        let lu:AStarPoint = null, ru:AStarPoint = null, ld:AStarPoint = null, rd:AStarPoint = null;

        if (point.IndexY>0)
        {
            up = this. m_Maparray[point.IndexX][ point.IndexY - 1];
        }

        if (point.IndexY<this.m_mapHeight-1)
        {
            down = this.m_Maparray[point.IndexX][point.IndexY + 1];
        }

        if (point.IndexX>0)
        {
            left= this.m_Maparray[point.IndexX-1][point.IndexY];
        }
        if (point.IndexX<this.m_mapWidth-1)
        {
            right = this.m_Maparray[point.IndexX +1][ point.IndexY];
        }

        if (up!=null&&left!=null)
        {
            lu = this.m_Maparray[point.IndexX-1][point.IndexY-1];
        }

        if (up != null && right != null)
        {
            ru = this.m_Maparray[point.IndexX+1][point.IndexY - 1];
        }
        if (down != null && left != null)
        {
            ld= this.m_Maparray[point.IndexX - 1][ point.IndexY +1];
        }
        if (down != null && right != null)
        {
            rd = this.m_Maparray[point.IndexX + 1][ point.IndexY +1];
        }

       let pointlist: Array<AStarPoint> = [];

       if (up != null && !up.IsWall)
       {
           pointlist.push(up);
       }
        //判断上下左右是否可以到达
        if (down!=null&&!down.IsWall)
        {
            pointlist.push(down);
        }
      
        if (left != null && !left.IsWall)
        {
            pointlist.push(left);
        }
        if (right != null && !right.IsWall)
        {
            pointlist.push(right);
        }
        //左上 左边和上边都不能为墙
        if (lu!=null&&lu.IsWall==false&&left.IsWall==false&&up.IsWall==false)
        {
            pointlist.push(lu);
        }
        //右上 右边和上边都不能为墙
        if (ru != null && ru.IsWall == false && right.IsWall == false && up.IsWall == false)
        {
            pointlist.push(ru);
        }
        //左下 左边和下边都不能为墙
        if (ld != null && ld.IsWall == false && left.IsWall == false && down.IsWall == false)
        {
            pointlist.push(ld);
        }
        //右下 右边和下边都不能为墙
        if (rd != null && rd.IsWall == false && right.IsWall == false && down.IsWall == false)
        {
            pointlist.push(rd);
        }
        return pointlist;

    }


      
public FindPathByBresenham_Line(start:AStarPoint,end:AStarPoint)  
{  
    let x1=start.IndexX,x2=end.IndexX,y1=start.IndexY,y2=end.IndexY;
     let dx = Math.abs(x2 - x1),
     dy = Math.abs(y2 - y1),
     yy = 0;
    if (dx < dy)
    {
        yy = 1;
    // this. swap( x1, y1);
       x1 ^= y1;
       y1 ^= x1;
       x1 ^= y1;
    // swap(&x2, &y2);    
       x2 ^= y2;
       y2 ^= x2;
       x2 ^= y2;
    // swap(&dx, &dy);
       dx ^= dy;
       dy ^= dx;
       dx ^= dy;
        
    }
    let ix = (x2 - x1) > 0 ? 1 : -1,
        iy = (y2 - y1) > 0 ? 1 : -1,
        cx = x1,
        cy = y1,
        n2dy = dy * 2,
        n2dydx = (dy - dx) * 2,
        d = dy * 2 - dx;
    let patharry:Array<AStarPoint>=[];
    if (yy) 
    { // 如果直线与 x 轴的夹角大于 45 度
        while (cx != x2) {
            if (d < 0) {
                d += n2dy;
            } else {
                cy += iy;
                d += n2dydx;
            }
            if (this.m_Maparray[cy][cx].IsWall) 
            {
                this.FindPath(start,end);
                return;    
            }
           patharry.push(this.m_Maparray[cy][cx]);
            // this.m_Maparray[cy][cx].ChangeColor(1,0,0,1);
            cx += ix;
        }
    } 
    else 
    { // 如果直线与 x 轴的夹角小于 45 度
        while (cx != x2) 
        {
            if (d < 0) {
                d += n2dy;
            } else {
                cy += iy;
                d += n2dydx;
            }
            if (this.m_Maparray[cx][cy].IsWall) 
            {
                this.FindPath(start,end);
                return;    
            }
            patharry.push(this.m_Maparray[cx][cy]);
            cx += ix;
        }
    }
    patharry.push(end);
    let lenght=patharry.length-1;
    for (let index = lenght; index > 0; index--) {
        const element = patharry[index];
        element.Parent=patharry[index-1];
        element.ChangeColor(index/lenght,0,0,1);
        // console.log("startIdx="+start.IndexX +"y="+start.IndexY+" endIdx="+end.IndexX+" y="+end.IndexY+" currentidx="+ element.IndexX+" y="+element.IndexY);
    }
} 

    public  FindPath(start:AStarPoint,end:AStarPoint)
    {
        let openList:AStarPoint[] = [];
        let closeList:AStarPoint[]= [];
        
        openList.push(start);

        while (openList.length>0)
        {
           let  point:AStarPoint =openList[0];
           for(let i=0;i<openList.length;i++)
           {//&&openList[i].H<point.H
                if(openList[i].GetF()<point.GetF())
                {
                    point = openList[i];
                }
           }

           let index:number=openList.indexOf(point);
           if(index<0){
            console.log("找不到路径");
            return;
           }
            openList.splice(index,1);
            closeList.push(point);

            //获取可以移动的周围点 
            let surroundPoints:Array<AStarPoint> =AStarMapManager.GetInstance().GetSurroundPoins(point);

            for(let i=0;i<surroundPoints.length;i++){
                if(closeList.lastIndexOf(surroundPoints[i])<0){
                    if(openList.lastIndexOf(surroundPoints[i])>-1){
                        let nowG :number=this.CalcG(surroundPoints[i],point);
                        if (nowG<surroundPoints[i].G)
                        {
                            surroundPoints[i].Parent=point;
                            surroundPoints[i].G=nowG;
                        }
                    }
                    //不在open列表中
                    else
                    {
                        surroundPoints[i].Parent = point;
                        this.CalcF(surroundPoints[i],end);
                        openList.push(surroundPoints[i]);
                    }
                }
            }
            //console.log(end);
            //结束点在开启列表中
            if (openList.indexOf(end)>-1)
            {
                break;
            }
        }
    }


    public  CalcF( now:AStarPoint, end:AStarPoint)
    {
        //F=G+H
        let hx:number =Math.abs(end.IndexX-now.IndexX);
        let hy:number=Math.abs(end.IndexY-now.IndexY);
        let h=hx+hy;
        //当前点到父亲节点的距离+父节点到目标点的距离
         let g:number = 0;
        if (now.Parent!=null)
        {
            g =this.CalcG(now,now.Parent);
            // Vector2.Distance(new Vector2(now.IndexX, now.IndexY), new Vector2(now.Parent.IndexX, now.Parent.IndexY)) + now.Parent.G ;
        }
        now.G = g;
        now.H = h;
    }


    public   CalcG( now:AStarPoint,parent:AStarPoint):number
    {
        let xvalue=Math.abs(now.IndexX-parent.IndexX);
        let yvalue=Math.abs(now.IndexY-parent.IndexY);      
        let distance:number=Math.sqrt(xvalue*xvalue+yvalue*yvalue);

       return distance;
    }


    public ResetArMap()
    {
        this.m_Maparray.forEach(cubeArray => {
            cubeArray.forEach(element => {
                element.ResetInfo();
                this.m_MapCubePool.Recycle(element);
            });
        });
        
        this.m_Maparray.length=0;
    }


    public DisableMapCube()
    {
        this.m_Maparray.forEach(cubeArray => {
            cubeArray.forEach(element => {
              element.MySprite.active=false;
            });
        });
    }




}